﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System.Text.RegularExpressions;
using System.Diagnostics;
using intelliEssay.Interop;
using intelliEssay.Interop.ParagraphStyle;



namespace intelliEssay.Core.DocumentStructure
{

    /// <summary>
    /// Enumerations of each part of a document
    /// </summary>
    public enum DocumentParts
    {
        /// <summary>
        /// 封面的列表
        /// </summary>
        CoverInfo,
        /// <summary>
        /// 封面
        /// </summary>
        Cover,
        /// <summary>
        /// 正文格式
        /// </summary>
        MainDocumentBody,
        /// <summary>
        /// 正文英文格式
        /// </summary>
        MainDocumentBodyInEnglish,
        /// <summary>
        /// 致谢格式
        /// </summary>
        Acknowledgement,
        /// <summary>
        /// 致谢内容格式
        /// </summary>
        AcknowledgementContent,
        /// <summary>
        /// 中文摘要格式
        /// </summary>
        abstractCN,
        /// <summary>
        /// 中文摘要内容格式
        /// </summary>
        abstractCN_Content,
        /// <summary>
        /// Optional field.
        /// </summary>
        abstractEN,
        /// <summary>
        /// keyword一词的格式
        /// </summary>
        abstractEN_Content,
        /// <summary>
        /// 关键字格式
        /// </summary>
        KeywordInChinese,
        /// <summary>
        /// 中文关键词内容
        /// </summary>
        KeywordInChineseContent,
        /// <summary>
        /// 英文关键词内容
        /// </summary>
        KeywordInEnglishContent,
        /// <summary>
        /// 英文关键字格式
        /// </summary>
        KeywordInEnglish,
        /// 带级标题格式
        /// </summary>
        LviParagraph,
        /// <summary>
        /// 参考文献格式
        /// </summary>
        Reference,
        /// <summary>
        /// 参考文献内容格式
        /// </summary>
        ReferencedContent,
        /// <summary>
        /// 目录格式
        /// </summary>
        Menu,
        /// <summary>
        /// 目录内容格式
        /// </summary>
        MenuContent,
        /// <summary>
        /// 附录
        /// </summary>
        Appendix,
        /// <summary>
        /// 附录内容
        /// </summary>
        AppendixContent,
        Announcement,
        AnnouncementContent
    }
    /// <summary>
    /// This class is used for extending DocumentParts's supported Extended methods
    /// <para>Meaningless to users</para>
    /// </summary>
    public static class DocumentPartsExtensions
    {
        /// <summary>
        /// Translate a DocumentPart instance to its readbale Chinese Representation.
        /// <para>For example, a  DocumentParts.Appendix will be translated to 附录.</para>
        /// </summary>
        /// <param name="dp">The documentPart instance whose name is to be translated</param>
        /// <param name="lvlIndex">This parameter is not used unless dp is LviParagraph</param>
        /// <returns></returns>
        public static string ToLocalizedString(this DocumentParts dp, int lvlIndex, string culturalInfo = "zh-cn")
        {
            if (culturalInfo == "zh-cn")
            {
                switch (dp)
                {
                    case DocumentParts.abstractCN:
                        return "中文摘要";
                    case DocumentParts.abstractCN_Content:
                        return "中文摘要内容";
                    case DocumentParts.abstractEN:
                        return "英文摘要";
                    case DocumentParts.abstractEN_Content:
                        return "英文摘要内容";
                    case DocumentParts.Acknowledgement:
                        return "致谢";
                    case DocumentParts.AcknowledgementContent:
                        return "致谢内容";
                    case DocumentParts.Appendix:
                        return "附录";
                    case DocumentParts.AppendixContent:
                        return "附录内容";
                    case DocumentParts.KeywordInChinese:
                        return "中文关键词";
                    case DocumentParts.KeywordInChineseContent:
                        return "中文关键词内容";
                    case DocumentParts.KeywordInEnglish:
                        return "英文关键词";
                    case DocumentParts.KeywordInEnglishContent:
                        return "英文关键词内容";
                    case DocumentParts.LviParagraph:
                        return lvlIndex + "级标题";
                    case DocumentParts.MainDocumentBody:
                        return "正文";
                    case DocumentParts.Menu:
                        return "目录";
                    case DocumentParts.MenuContent:
                        return "目录内容";
                    case DocumentParts.Reference:
                        return "参考文献";
                    case DocumentParts.ReferencedContent:
                        return "参考文献内容";
                    case DocumentParts.Announcement:
                        return "申明";
                    case DocumentParts.AnnouncementContent:
                        return "申明内容";
                    default:
                        return dp.ToString();
                }

            }
            return dp.ToString();
        }
        /// <summary>
        /// Gets the index of documentPart in the documentPart collection.
        /// Note the ToArray() is called on the IEnumerable.
        /// </summary>
        /// <param name="dpCollection"></param>
        /// <param name="dp"></param>
        /// <returns></returns>
        public static int IndexOf(this IEnumerable<DocumentParts> dpCollection, DocumentParts dp)
        {
            var array = dpCollection.ToArray<DocumentParts>();
            for (int i = 0; i < array.Length; i++)
            {
                if (array[i] == dp)
                {
                    return i;
                }
            }
            return array.Length - 1;
        }
    }
    /// <summary>
    /// Represents the structure of the document.
    /// </summary>
    public class DocumentStructure
    {
        /// <summary>
        /// Checks if a file is of OpenXml format
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static bool IsValidOpenXmlFile(string path)
        {
            try
            {
                var wordProc = WordprocessingDocument.Open(path, false);
                wordProc.Dispose();
                return true;
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// Maps the index of pattern into Cover information
        /// </summary>
        /// <param name="pattern"></param>
        /// <param name="isMultiMajor"></param>
        /// <returns></returns>
        public static int GetItemMappedID(string pattern, bool isMultiMajor)
        {
            return DocumentStructureHelper.GetItemMapped(!isMultiMajor, pattern);
        }
        /// <summary>
        /// Contains information on bullet lists
        /// </summary>
        public List<Paragraph>[] BulletLists = new List<Paragraph>[10];
        /// <summary>
        /// Contains information on paragraph's numbering information
        /// </summary>
        public ParagraphNumbering ParagraphNumbering = new ParagraphNumbering();
        /// <summary>
        /// This field does not point to any paragraph in the document,
        /// however, it points to anywhere that doesn't belong to any part in the processed document.
        /// <para>Example usages of this field is missing key element or page setup error</para>
        /// </summary>
        public Paragraph NoWherePointer = new Paragraph();
        /// <summary>
        /// This documentElementSequence is generated when the task is initialized.
        /// This property is generated according to the documentStructure itself.
        /// </summary>
        public DocumentKeyElementSequence documentElementSequence
        {
            get;
            set;
        }
        /// <summary>
        /// Gets the paragraph's index
        /// </summary>
        public Dictionary<Paragraph, int> ParagraphSequenceDict = new Dictionary<Paragraph, int>();
        /// <summary>
        /// 是否是多专业
        /// </summary>
        public bool IsMultiMajor = false;
        /// <summary>
        ///正文内容
        /// </summary>
        public List<Paragraph> MainDocumentBody = new List<Paragraph>();
        /// <summary>
        /// 致谢二字所在段落
        /// </summary>
        public Paragraph Acknowledgement;
        /// <summary>
        /// 致谢内容所在段落列表
        /// </summary>
        public List<Paragraph> AcknowledgementContent = new List<Paragraph>();
        /// <summary>
        /// 摘要二字所在段落
        /// </summary>
        public Paragraph ChineseAbstract;
        /// <summary>
        /// 摘要内容
        /// </summary>
        public List<Paragraph> ChineseAbstractContents = new List<Paragraph>();
        /// <summary>
        /// 英文摘要二字所在段落
        /// </summary>
        public Paragraph EnglishAbstract;
        /// <summary>
        /// 英文摘要内容
        /// </summary>
        public List<Paragraph> EnglishAbstractContents = new List<Paragraph>();
        /// <summary>
        /// 中文关键词和关键词内容，包括“关键字”三字
        /// </summary>
        public List<Paragraph> KeywordsInChinese = new List<Paragraph>();
        /// <summary>
        /// Keyword in English,包括'keyword'
        /// </summary>
        public List<Paragraph> KeywordsInEnglish = new List<Paragraph>();
        public List<int> TitleLevelMetaList = new List<int>();
        /// <summary>
        /// 记录第i级标题。一级标题，用大写中文‘一’、‘二’标注； 二级标题，用（一）、（二）标注；
        /// 三级标题，用1、2标注；四级标题，用(1)、(2)标注；五级标题，用①、②标注。0收录所有标题。
        /// </summary>
        public List<Paragraph>[] LviParagraph = new List<Paragraph>[10];
        /// <summary>
        /// 参考文献四个字所在段落
        /// </summary>
        public Paragraph Reference;
        /// <summary>
        /// 参考文献条目
        /// </summary>
        public List<Paragraph> ReferencedDocuments = new List<Paragraph>();
        /// <summary>
        /// 目录二字所在段落
        /// </summary>
        public Paragraph Menu;
        /// <summary>
        /// 目录条目
        /// </summary>
        public List<Paragraph> MenuList = new List<Paragraph>();
        /// <summary>
        /// 封面中“南开大学”4个字所在段落
        /// </summary>
        public Paragraph Cover;
        /// <summary>
        /// 单学位毕业论文封面中的11个项目（不包括空自然段）
        /// </summary>
        public Paragraph[] CoverSingleMajorInfoList = new Paragraph[11];
        /// <summary>
        ///  双学位毕业论文封面中的14个项目（不包括空自然段）
        /// </summary>
        public Paragraph[] CoverMultiMajorInfoList = new Paragraph[14];
        /// <summary>
        /// 附录
        /// </summary>
        public Paragraph Appendix;
        /// <summary>
        /// 附录内容
        /// </summary>
        public List<Paragraph> AppendixContent = new List<Paragraph>();
        /// <summary>
        /// 所有的段落
        /// </summary>
        public Paragraph[] AllParagraph;

        public Paragraph Announcement;

        public List<Paragraph> AnnouncementContent = new List<Paragraph>();
        internal Dictionary<string, Style> styleDict = new Dictionary<string, Style>();

        /// <summary>
        /// 构造函数
        /// </summary>
        public DocumentStructure()
        {
            for (int i = 0; i < 14; i++)
            {
                if (i < 10)
                {
                    LviParagraph[i] = new List<Paragraph>();
                    BulletLists[i] = new List<Paragraph>();
                }
                if (i < 11)
                {
                    CoverSingleMajorInfoList[i] = new Paragraph();
                }
                if (i < 14)
                {
                    CoverMultiMajorInfoList[i] = new Paragraph();
                }
            }
        }
        /// <summary>
        /// Gets an instance of DocumentStructure Class from a WordprocessingDocument
        /// </summary>
        /// <param name="doc"></param>
        /// <returns></returns>
        public static DocumentStructure GenerateDocumentStructure(WordprocessingDocument wd, HostApplications ha = HostApplications.MsOffice2010)
        {
            DocumentStructure ds = new DocumentStructure();
            var Styles = wd.MainDocumentPart.StyleDefinitionsPart.Styles.ChildElements;
            IEnumerable<Style> styles = (from style in wd.MainDocumentPart.StyleDefinitionsPart.Styles.ChildElements where style.LocalName == "style" select style).Cast<Style>();
            if (ha == HostApplications.MsOffice2010)
            {
                try
                {
                    styles = styles.Union<Style>(wd.MainDocumentPart.StylesWithEffectsPart.Styles.ChildElements.Where<OpenXmlElement>(
                    o =>
                    {
                        return o.LocalName == "style";
                    }).Cast<Style>(), new StyleComparer());
                }
                catch
                {
                }
            }
            foreach (Style s in styles)
            {
                ds.styleDict.Add(s.StyleId, s);
            }
            #region solve the cover
            Document doc = wd.MainDocumentPart.Document;
            Body docBody =
            doc.MainDocumentPart.Document.Body;
            List<Paragraph> Paras = docBody.ChildElements.OfType<Paragraph>().ToList();
            bool isMultiMajor = false;
            int SkipCount = 0;
            int SkipParagraphFieldCodeCheckCount = 0;
            Paragraph p;
            System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("^.+[\\…\\.]*\\d+", RegexOptions.Compiled);
            System.Text.RegularExpressions.Regex reg1 = new System.Text.RegularExpressions.Regex("^\\s*\\[\\d+\\]", RegexOptions.Compiled);
            System.Text.RegularExpressions.Regex[] titlesReg = new Regex[] {new Regex("^\\s*第\\s*([\u4e00-\u9fa5\\d\\w]+)\\s*章\\s+|^\\s*([一二三四五六七八九十百千万亿]+)\\s*[、\\.]",RegexOptions.Compiled),
                                                                                                      new Regex("^\\s*[\\(（]\\s*([一二三四五六七八九十百千万亿])+\\s*[）\\)]*",RegexOptions.Compiled),
                                                                                                      new Regex("^\\s*(\\d+)\\s*[、\\.。]\\s*[^\\d]",RegexOptions.Compiled),
                                                                                                      new Regex("^\\s*[\\(（]s*(\\d+)\\s*[\\)）]*",RegexOptions.Compiled),
                                                                                                      new Regex("^\\s*([①②③④⑤⑥⑦⑧⑨⑩]+)\\s*",RegexOptions.Compiled),
                                                                                                      new Regex("^\\s*\\d+(\\.\\d+){0,}\\.{0,1}\\s*[^\\-\\d]",RegexOptions.Compiled)};
            var numDefPt = wd.MainDocumentPart.NumberingDefinitionsPart;
            Numbering numbering = null;
            try
            {
                numbering = numDefPt.Numbering;
            }
            catch
            {
            }
            IEnumerable<AbstractNum> abstrNumCollection = null;
            IEnumerable<NumberingInstance> instNumCollection = null;
            if (numDefPt != null)
            {
                abstrNumCollection = numbering.ChildElements.Where<OpenXmlElement>(o =>
                    {
                        return o.GetType() == typeof(AbstractNum);
                    }).Cast<AbstractNum>();
                instNumCollection = numbering.ChildElements.Where<OpenXmlElement>(o =>
                     {
                         return o.GetType() == typeof(NumberingInstance);
                     }).Cast<NumberingInstance>();
            }







            for (int i = 0; i < Paras.Count(); i++)
            {
                p = Paras[i];

                #region remedy for MenuList FieldCode

                if (p.InnerText.Contains("2011-5-8"))
                {
                }

                if (SkipParagraphFieldCodeCheckCount != 0 || !DocumentStructureHelper.IsParagraphInvalid(p))
                {
                    if (SkipParagraphFieldCodeCheckCount != 0)
                    {
                        SkipParagraphFieldCodeCheckCount--;
                    }
                    ds.ParagraphSequenceDict.Add(Paras[i], ds.ParagraphSequenceDict.Keys.Count);
                }
                #endregion
                NumberFormatValues fmtValue;
                int _LvlNumbering = ds.GetParagraphLevelNumbering(p, wd, instNumCollection, abstrNumCollection, out fmtValue);
                if (_LvlNumbering != -1 && DocumentStructureHelper.IsParagraphInvalid(p, DocumentStructureHelper.ParagraphValidityCheckOptions.IgnoreWPSCode) == false && ds.MenuList.Contains(p) == false && ds.ReferencedDocuments.Contains(p) == false)
                {
                    if (fmtValue != NumberFormatValues.Bullet && DocumentStructureHelper.IsParagraphEndsWithPunctuation(p) == false)
                    {
                        ds.LviParagraph[_LvlNumbering].Add(p);
                        ds.TitleLevelMetaList.Add(_LvlNumbering);
                        ds.LviParagraph[0].Add(p);
                        ds.ParagraphNumbering.ParagraphNumberingLevel[p] = _LvlNumbering;
                    }
                    else if (fmtValue == NumberFormatValues.Bullet)
                    {
                        ds.BulletLists[_LvlNumbering].Add(p);
                        ds.BulletLists[0].Add(p);
                    }
                    continue;
                }

                int _LvlNonNumbering = ds.GetParagraphLevelNonNumbering(p, titlesReg);
                if (_LvlNonNumbering != -1 && DocumentStructureHelper.IsParagraphInvalid(p, DocumentStructureHelper.ParagraphValidityCheckOptions.IgnoreWPSCode) == false && ds.MenuList.Contains(p) == false && ds.ReferencedDocuments.Contains(p) == false)
                {
                    ds.LviParagraph[_LvlNonNumbering].Add(p);
                    ds.TitleLevelMetaList.Add(_LvlNonNumbering);
                    ds.LviParagraph[0].Add(p);
                    ds.ParagraphNumbering.ParagraphNumberingLevel[p] = _LvlNonNumbering;
                    continue;
                }

                int _LvlOutline = DocumentStructureHelper.GetParagraphOutlineLvl(p, ds.styleDict);
                if (_LvlOutline != -1 && DocumentStructureHelper.IsParagraphInvalid(p) == false && DocumentStructureHelper.IsParagraphKeyword(p) == false)
                {
                    ds.LviParagraph[_LvlOutline].Add(p);
                    ds.LviParagraph[0].Add(p);
                    ds.TitleLevelMetaList.Add(_LvlOutline);
                    ds.ParagraphNumbering.ParagraphNumberingLevel[p] = _LvlOutline;
                }

                if (SkipCount != 0)
                {
                    SkipCount--;
                    continue;
                }

                if (DocumentStructureHelper.IsParagraphInvalid(p))
                {
                    continue;
                }

                if (p.InnerText.Replace(" ", "") == "南开大学")
                {
                    ds.Cover = p;
                    continue;
                }
                if (p.InnerText == "本 科 生 毕 业 论 文（设 计）")
                {
                    ds.CoverMultiMajorInfoList[0] = ds.CoverSingleMajorInfoList[0] = p;
                    continue;
                }
                if (p.InnerText == "(双学位专用)")
                {
                    isMultiMajor = true;
                    ds.CoverMultiMajorInfoList[1] = p;
                    ds.IsMultiMajor = true;
                    continue;
                }
                try
                {
                    if (isMultiMajor)
                    {
                        ds.CoverMultiMajorInfoList[DocumentStructureHelper.GetItemMapped(false, p.InnerText)] = p;
                        continue;
                    }
                    else
                    {
                        ds.CoverSingleMajorInfoList[DocumentStructureHelper.GetItemMapped(true, p.InnerText)] = p;
                        continue;
                    }
                }
                catch
                {
                }




















                if (p.InnerText.Replace(" ", "").Replace("\t", "").Replace("　", "") == "摘要")
                {
                    ds.ChineseAbstract = p;
                    for (int x = i + 1; x < Paras.Count; x++)
                    {
                        if (ds.Context_ShouldBreakContentIteration(Paras[x], wd, instNumCollection, abstrNumCollection, titlesReg)) break;
                        SkipCount++;
                        if (!DocumentStructureHelper.IsParagraphInvalid(Paras[x]))
                        {
                            ds.ChineseAbstractContents.Add(Paras[x]);
                        }
                    }
                    continue;
                }








                if (p.InnerText.Replace(" ", "").ToLowerInvariant() == "abstract")
                {
                    ds.EnglishAbstract = p;
                    for (int x = i + 1; x < Paras.Count; x++)
                    {
                        if (!ds.Context_ShouldBreakContentIteration(Paras[x], wd, instNumCollection, abstrNumCollection, titlesReg))
                        {
                            SkipCount++;
                            if (!DocumentStructureHelper.IsParagraphInvalid(Paras[x]))
                            {
                                ds.EnglishAbstractContents.Add(Paras[x]);
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                    continue;
                }









                if (p.InnerText.Replace(" ", "").Replace("\t", "").Replace("　", "") == "目录")
                {
                    ds.Menu = p;
                    for (int x = i + 1; x < Paras.Count; x++)
                    {
                        if (Paras[x].InnerText.Trim() == "" || Paras[x].InnerText.Contains("..") || Paras[x].InnerText.Contains("……") || Paras[x].InnerText.Contains("PAGEREF _Toc"))
                        {
                            SkipCount++;
                            if (!DocumentStructureHelper.IsParagraphInvalid(Paras[x], DocumentStructureHelper.ParagraphValidityCheckOptions.IgnoreFieldCode))
                            {
                                SkipParagraphFieldCodeCheckCount++;
                                ds.MenuList.Add(Paras[x]);
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                    continue;
                }





                if (p.InnerText.Replace(" ", "").Replace("\t", "").Replace("　", "") == "附录")
                {
                    ds.Appendix = p;
                    for (int x = i + 1; x < Paras.Count; x++)
                    {
                        if (!ds.Context_ShouldBreakContentIteration(Paras[x], wd, instNumCollection, abstrNumCollection, titlesReg))
                        {
                            SkipCount++;
                            if (!DocumentStructureHelper.IsParagraphInvalid(Paras[x]))
                            {
                                ds.AppendixContent.Add(Paras[x]);
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                    continue;
                }






                if (p.InnerText.Replace(" ", "") == "参考文献")
                {
                    ds.Reference = p;
                    for (int x = i + 1; x < Paras.Count; x++)
                    {
                        string temp = Paras[x].InnerText;
                        if (!ds.Context_ShouldBreakContentIteration(Paras[x], wd, instNumCollection, abstrNumCollection, titlesReg, true))
                        {
                            SkipCount++;
                            if (!DocumentStructureHelper.IsParagraphInvalid(Paras[x]))
                            {
                                ds.ReferencedDocuments.Add(Paras[x]);
                            }
                        }
                        else
                        {
                            break;
                        }

                    }
                    continue;
                }






                if (p.InnerText.Replace(" ", "") == "关于南开大学本科生毕业论文（设计）的声明")
                {
                    ds.Announcement = p;
                    for (int x = i + 1; x < Paras.Count; x++)
                    {
                        if (ds.Context_ShouldBreakContentIteration(Paras[x], wd, instNumCollection, abstrNumCollection, titlesReg)) break;
                        SkipCount++;
                        if (!DocumentStructureHelper.IsParagraphInvalid(Paras[x]))
                        {
                            ds.AnnouncementContent.Add(Paras[x]);
                        }
                    }
                    continue;
                }






                if (p.InnerText.Replace(" ", "").Replace("\t", "").Replace(" ", "") == "致谢")
                {
                    ds.Acknowledgement = p;
                    for (int x = i + 1; x < Paras.Count; x++)
                    {
                        if (ds.Context_ShouldBreakContentIteration(Paras[x], wd, instNumCollection, abstrNumCollection, titlesReg)) break;
                        SkipCount++;
                        if (!DocumentStructureHelper.IsParagraphInvalid(Paras[x]))
                        {
                            ds.AcknowledgementContent.Add(Paras[x]);
                        }
                    }
                    continue;
                }













                if (p.InnerText.Replace(" ", "").StartsWith("关键词：") || p.InnerText.Replace(" ", "").StartsWith("关键词:") || p.InnerText.Replace(" ", "").StartsWith("关键字：") || p.InnerText.Replace(" ", "").StartsWith("关键字:") || p.InnerText.Replace(" ", "").StartsWith("关键字") || p.InnerText.Replace(" ", "").StartsWith("关键词"))
                {
                    ds.KeywordsInChinese.Add(p);
                    for (int x = i + 1; x < Paras.Count; x++)
                    {
                        if (ds.Context_ShouldBreakContentIteration(Paras[x], wd, instNumCollection, abstrNumCollection, titlesReg)) break;
                        SkipCount++;
                        if (!DocumentStructureHelper.IsParagraphInvalid(Paras[x]))
                        {
                            ds.KeywordsInChinese.Add(Paras[x]);
                        }
                    }
                    continue;
                }










                if (p.InnerText.Replace(" ", "").ToLowerInvariant().StartsWith("keywords:") || p.InnerText.Replace(" ", "").ToLowerInvariant().StartsWith("keywords：") || p.InnerText.Replace(" ", "").ToLowerInvariant().StartsWith("keyword：") || p.InnerText.Replace(" ", "").ToLowerInvariant().StartsWith("keyword:"))
                {
                    ds.KeywordsInEnglish.Add(p);
                    for (int x = i + 1; x < Paras.Count; x++)
                    {
                        if (ds.Context_ShouldBreakContentIteration(Paras[x], wd, instNumCollection, abstrNumCollection, titlesReg)) break;
                        SkipCount++;
                        if (!DocumentStructureHelper.IsParagraphInvalid(Paras[x]))
                        {
                            ds.KeywordsInEnglish.Add(Paras[x]);
                        }
                    }
                    continue;
                }
                ds.MainDocumentBody.Add(p);
            }
            ds.AllParagraph = ds.ParagraphSequenceDict.Keys.ToArray();
            #endregion
            return ds;
        }

        private static Regex findLargestLvl = new Regex("(\\d+)", RegexOptions.Compiled);
        private static int GetLargestLevel(string lvlTxt)
        {
            if (!findLargestLvl.IsMatch(lvlTxt))
            {
                return -1;
            }
            if (lvlTxt.EndsWith("."))
            {
                return lvlTxt.Split('.').Length - 1;
            }
            return lvlTxt.Split('.').Length;
        }
        /// <summary>
        /// Gets the Paragraph's associated numbering information, considering both Auto-Numbering and Manual Numbering
        /// </summary>
        /// <param name="p">the paragraph whose numbering is to be retrieved</param>
        /// <param name="NumberingProperty">true if the paragraph is auto-numbered</param>
        /// <returns>the string format</returns>
        public string GetParagraphNumbering(Paragraph p, out bool NumberingProperty)
        {
            try
            {
                NumberingProperty = true;
                return ParagraphNumbering.GetParagraphLevelStringUnformated(p);
            }
            catch
            {
                NumberingProperty = false;
                return p.InnerText;
            }
        }

        private bool Context_ShouldBreakContentIteration(Paragraph paragraph, WordprocessingDocument wd, IEnumerable<NumberingInstance> instNumCollection, IEnumerable<AbstractNum> abstractNumCollection, Regex[] titleReg, bool SkipMenuListNumberingCheck = false)
        {
            NumberFormatValues fmtValue = NumberFormatValues.None;
            if (!SkipMenuListNumberingCheck)
            {
                bool flag1 = DocumentStructureHelper.IsParagraphKeyword(paragraph) || GetParagraphLevelNonNumbering(paragraph, titleReg) != -1;
                bool flag2 = DocumentStructureHelper.IsParagraphEndsWithPunctuation(paragraph);
                int lvl = GetParagraphLevelNumbering(paragraph, wd, instNumCollection, abstractNumCollection, out fmtValue, true);
                int outlineLvl = DocumentStructureHelper.GetParagraphOutlineLvl(paragraph, styleDict);
                return flag1 || (lvl != -1 && flag2 == false && fmtValue != NumberFormatValues.Bullet) || outlineLvl != -1;
            }
            else
            {
                return DocumentStructureHelper.IsParagraphKeyword(paragraph);
            }
        }

        /// <summary>
        /// Gets the Paragraph's Level if it is set using Numbering in the context of the document structure.
        /// </summary>
        /// <param name="paragraph"></param>
        /// <param name="wd"></param>
        /// <param name="instNumCollection"></param>
        /// <param name="abstrNumCollection"></param>
        /// <param name="peek"></param>
        /// <returns></returns>
        private int GetParagraphLevelNumbering(Paragraph paragraph, WordprocessingDocument wd, IEnumerable<NumberingInstance> instNumCollection, IEnumerable<AbstractNum> abstrNumCollection, out NumberFormatValues fmtValue, bool peek = false)
        {
            fmtValue = NumberFormatValues.None;
            if (paragraph.InnerText == "" || DocumentStructureHelper.IsParagraphEndsWithPunctuation(paragraph)) return -1;
            int candidateNumbering = DocumentStructureHelper.GetParagraphLevelNumbering(paragraph, wd, instNumCollection, abstrNumCollection, ParagraphNumbering, styleDict, out fmtValue, peek);
            if (candidateNumbering != -1)
            {
                switch (ParagraphNumbering.ParagraphNumberingFmtValues[paragraph])
                {
                    case NumberFormatValues.JapaneseCounting:
                        {
                            if (ParagraphNumbering.ParagraphNumberingLevelText[paragraph].Contains("(") || ParagraphNumbering.ParagraphNumberingLevelText[paragraph].Contains(")") ||
                                ParagraphNumbering.ParagraphNumberingLevelText[paragraph].Contains("（") || ParagraphNumbering.ParagraphNumberingLevelText[paragraph].Contains("）"))
                            {
                                return 2;
                            }
                            return 1;
                        }
                    case NumberFormatValues.DecimalEnclosedCircle:
                    case NumberFormatValues.DecimalEnclosedCircleChinese:
                    case NumberFormatValues.DecimalEnclosedFullstop:
                        {
                            return 5;
                        }
                    case NumberFormatValues.Decimal:
                        {
                            if (ParagraphNumbering.ParagraphNumberingLevelText[paragraph].Contains("(") || ParagraphNumbering.ParagraphNumberingLevelText[paragraph].Contains(")") ||
                                ParagraphNumbering.ParagraphNumberingLevelText[paragraph].Contains("（") || ParagraphNumbering.ParagraphNumberingLevelText[paragraph].Contains("）"))
                            {
                                return 4;
                            }
                            else if (ParagraphNumbering.ParagraphNumberingLevelText[paragraph].Trim() == "%1.")
                            {
                                return 3;
                            }
                            return candidateNumbering;
                        }
                    default:
                        {
                            return candidateNumbering;
                        }
                }
            }
            return candidateNumbering;
        }
        private int GetParagraphLevelNonNumbering(Paragraph paragraph, Regex[] titlesReg)
        {
            if (DocumentStructureHelper.IsParagraphEndsWithPunctuation(paragraph))
            {
                return -1;
            }
            if (paragraph == null)
            {
                return -1;
            }
            // NumberFormatValues.ChineseCounting;
            // NumberFormatValues.JapaneseCounting;
            //System.Text.RegularExpressions.Regex[] titlesReg = new Regex[] {new Regex("^\\s*第\\s*[\u4e00-\u9fa5\\d\\w]*\\s*章\\s+|^\\s*[一二三四五六七八九十百千万亿]+\\s*[、\\.]",RegexOptions.Compiled),
            //                                                                                          new Regex("^\\s*[\\(（]\\s*[一二三四五六七八九十百千万亿\\d]+\\s*[）\\)]*",RegexOptions.Compiled),
            //                                                                                          new Regex("^\\s*\\d+\\s*[、\\.。]\\s*[^\\d]",RegexOptions.Compiled),
            //                                                                                          new Regex("^\\s*[\\(（]s*[\\d]+\\s*[\\)）]*",RegexOptions.Compiled),
            //                                                                                          new Regex("^\\s*[①②③④⑤⑥⑦⑧⑨⑩]+\\s*",RegexOptions.Compiled),
            //                                                                                          new Regex("^\\s*\\d+\\.(\\d+\\.){0,}\\d",RegexOptions.Compiled)};
            if (paragraph.InnerText.Trim() == "")
            {
                return -1;
            }
            Paragraph p = paragraph;
            for (int x = 0; x < titlesReg.Length - 1; x++)
            {
                if (titlesReg[x].IsMatch(p.InnerText))
                {
                    Match mtch = titlesReg[x].Match(p.InnerText);
                    ParagraphNumbering.ParagraphNumberingLevelText[paragraph] = mtch.Value.Replace(mtch.Groups[1].Value, "%1");
                    ParagraphNumbering.ParagraphNumberingDecimalFmt[paragraph] = Translate(mtch.Groups[1].Value) + ".";
                    switch (x + 1)
                    {
                        case 1:
                        case 2:
                            ParagraphNumbering.ParagraphNumberingFmtValues[paragraph] = NumberFormatValues.ChineseCountingThousand;
                            break;
                        case 3:
                        case 4:
                            ParagraphNumbering.ParagraphNumberingFmtValues[paragraph] = NumberFormatValues.Decimal;
                            break;
                        case 5:
                            ParagraphNumbering.ParagraphNumberingFmtValues[paragraph] = NumberFormatValues.JapaneseCounting;
                            break;
                        default:
                            break;
                    }
                    return x + 1;
                }
            }
            Match mc = titlesReg.Last<Regex>().Match(p.InnerText);
            if (mc.Success)
            {
                ParagraphNumbering.ParagraphNumberingFmtValues[paragraph] = NumberFormatValues.Decimal;
                int lvl = GetLargestLevel(mc.Value);
                StringBuilder sb = new StringBuilder();
                for (int i = 1; i <= lvl; i++)
                {
                    sb.Append(i + "%.");
                }
                if (mc.Value.EndsWith(".") == false)
                {
                    sb.Remove(sb.Length - 1, 1);
                }
                ParagraphNumbering.ParagraphNumberingDecimalFmt[paragraph] = mc.Value;
                ParagraphNumbering.ParagraphNumberingLevelText[paragraph] = sb.ToString();
                return lvl;
            }
            return -1;
        }

        static int ParseCHN(char c)
        {
            switch (c)
            {
                case '一':
                    return 1;
                case '二':
                    return 2;
                case '三':
                    return 3;
                case '四':
                    return 4;
                case '五':
                    return 5;
                case '六':
                    return 6;
                case '七':
                    return 7;
                case '八':
                    return 8;
                case '九':
                    return 9;
                case '十':
                    return 10;
                case '百':
                    return 100;
                case '千':
                    return 1000;
                case '万':
                    return 10000;
                case '亿':
                    return 100000000;
                case '零':
                    return 0;
                default:
                    return -1;
            }
        }

        private static string Translate(string p)
        {
            int currentIdx = -1;
            int currentAccumulator = 0;
            int accumulator = 0;
            int lastIdx = 0;
            for (int i = 0; i < p.Length; i++)
            {
                char currentC = p[i];
                currentIdx = ParseCHN(currentC);
                if (currentIdx == -1) return p;
                if (currentIdx < 10)
                {
                    currentAccumulator = currentIdx;
                }
                else
                {

                    if (currentAccumulator == 0)
                    {
                        currentAccumulator = 1;
                    }
                    if (currentIdx >= lastIdx && lastIdx != 0)
                    {
                        accumulator += currentAccumulator;
                        accumulator *= currentIdx;
                    }
                    else
                    {
                        currentAccumulator *= currentIdx;
                        accumulator += currentAccumulator;
                    }
                    currentAccumulator = 0;
                    lastIdx = currentIdx;
                }
            }
            accumulator += currentAccumulator;
            return accumulator.ToString();
        }
        /// <summary>
        /// 寻找离标题段落最近的父段落，如果参数是一级标题，则返回自身；如果参数不是标题，则返回null.
        /// </summary>
        /// <param name="index"></param>
        /// <param name="exactlyOneLevel">是否只寻找比它小一级的标题，缺省为否。即，若传入的标题为5级，则传出的标题段落要么为4级，要么为空</param>
        /// <remarks>This method is not tested</remarks>
        /// <returns></returns>
        public Paragraph GetNearestLowerLevelParagraph(Paragraph p, bool exactlyOneLevel = true)
        {
            //retireve the level of the paragraph
            int idx = LviParagraph[0].IndexOf(p);
            if (idx < 0) return null;
            int plvl = TitleLevelMetaList[idx];
            int targetLvl = plvl == 1 ? 1 : plvl - 1;
            if (targetLvl == 1) return p;
            /*return TitleLevelMetaList.Last<int>(o =>
                {
                    o < plvl;
                });
             * not a good one - enumeration from the last
             */
            for (int i = idx - 1; i >= 0; i--)
            {
                if (exactlyOneLevel == false)
                    if (TitleLevelMetaList[i] < plvl)
                        return LviParagraph[0][i];
                if (exactlyOneLevel == true)
                    if (TitleLevelMetaList[i] == targetLvl)
                        return LviParagraph[0][i];
            }
            return null;
        }
        /// <summary>
        /// 寻找某级标题的父段落
        /// </summary>
        /// <param name="p"></param>
        /// <param name="exactlyOneLevel">只寻找一级</param>
        /// <returns></returns>
        public int GetNearestLowerLevelParagraphLevel(Paragraph p, bool exactlyOneLevel = true)
        {
            Paragraph parent = GetNearestLowerLevelParagraph(p, exactlyOneLevel);
            return LviParagraph[0].IndexOf(parent);
        }

        /// <summary>
        /// Gets the index of the first paragraph of the DocumentParts dp in the document, if one documentpart contains multiple paragraphs,
        /// <para>the index of the first paragraoh is returned</para>
        /// </summary>
        /// <param name="dp">the part whose index is in query</param>
        /// <param name="length">the length of the type of the document part, usually it will be 1, but when the document part spans accorss multiple paragraphs, length indicates how many paragraphs following it belongs to the same element type</param>
        /// <returns></returns>
        public int? GetDocumentKeyElementIndexByName(DocumentParts dp, out int length)
        {
            length = 1;
            try
            {
                switch (dp)
                {
                    case DocumentParts.KeywordInChinese:
                    case DocumentParts.KeywordInChineseContent:
                        length = KeywordsInChinese.Count;
                        return ParagraphSequenceDict[KeywordsInChinese.First()];
                    case DocumentParts.KeywordInEnglish:
                    case DocumentParts.KeywordInEnglishContent:
                        length = KeywordsInEnglish.Count;
                        return ParagraphSequenceDict[KeywordsInEnglish.First()];
                    case DocumentParts.abstractCN:
                        return ParagraphSequenceDict[ChineseAbstract];
                    case DocumentParts.abstractCN_Content:
                        length = ChineseAbstractContents.Count;
                        return ParagraphSequenceDict[ChineseAbstractContents.First()];
                    case DocumentParts.abstractEN_Content:
                        length = EnglishAbstractContents.Count;
                        return ParagraphSequenceDict[EnglishAbstractContents.First()];
                    case DocumentParts.abstractEN:
                        return ParagraphSequenceDict[EnglishAbstract];
                    case DocumentParts.Acknowledgement:
                        return ParagraphSequenceDict[Acknowledgement];
                    case DocumentParts.AcknowledgementContent:
                        length = AcknowledgementContent.Count;
                        return ParagraphSequenceDict[AcknowledgementContent.First()];
                    case DocumentParts.Menu:
                        return ParagraphSequenceDict[Menu];
                    case DocumentParts.MenuContent:
                        length = MenuList.Count;
                        return ParagraphSequenceDict[MenuList.First()];
                    case DocumentParts.Reference:
                        return ParagraphSequenceDict[Reference];
                    case DocumentParts.ReferencedContent:
                        length = ReferencedDocuments.Count;
                        return ParagraphSequenceDict[ReferencedDocuments.First()];
                    case DocumentParts.MainDocumentBody:
                    case DocumentParts.MainDocumentBodyInEnglish:
                        length = MainDocumentBody.Count;
                        return ParagraphSequenceDict[MainDocumentBody.First()];
                    case DocumentParts.CoverInfo:
                        length = IsMultiMajor ? CoverMultiMajorInfoList.Length : CoverSingleMajorInfoList.Length;
                        return IsMultiMajor ? ParagraphSequenceDict[CoverMultiMajorInfoList.First()] : ParagraphSequenceDict[CoverSingleMajorInfoList.First()];
                    case DocumentParts.Cover:
                        length = 1;
                        return ParagraphSequenceDict[Cover];
                    case DocumentParts.Appendix:
                        length = 1;
                        return ParagraphSequenceDict[Appendix];
                    case DocumentParts.AppendixContent:
                        length = AppendixContent.Count;
                        return ParagraphSequenceDict[AppendixContent.First()];
                    case DocumentParts.Announcement:
                        length = 1;
                        return ParagraphSequenceDict[Announcement];
                    case DocumentParts.AnnouncementContent:
                        length = AnnouncementContent.Count;
                        return ParagraphSequenceDict[AnnouncementContent.First()];
                    default:
                        length = 0;
                        return null;
                }
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Gets the first paragraph that belongs to the documentParts in the document Structure
        /// </summary>
        /// <param name="dp">the type of paragraph to retrieve</param>
        /// <returns></returns>
        public Paragraph GetDocumentKeyElementParagraphByName(DocumentParts dp)
        {
            try
            {
                int length;
                return AllParagraph[GetDocumentKeyElementIndexByName(dp, out length).Value];
            }
            catch
            {
                return null;
            }
        }
        /// <summary>
        /// Gets a list containing all paragraphs that belong to KeyElement dp.
        /// </summary>
        /// <param name="dp">the keyelement's name</param>
        /// <param name="lvl">this parameter is not used if dp!=documentparts.lviParagraph</param>
        /// <returns></returns>
        public List<Paragraph> GetDocumentKeyElementList(DocumentParts dp, int lvl)
        {
            if (dp == DocumentParts.LviParagraph)
            {
                return LviParagraph[lvl];
            }
            else
            {
                switch (dp)
                {
                    case DocumentParts.KeywordInChinese:
                    case DocumentParts.KeywordInChineseContent:
                        return KeywordsInChinese;
                    case DocumentParts.KeywordInEnglish:
                    case DocumentParts.KeywordInEnglishContent:
                        return KeywordsInEnglish;
                    case DocumentParts.abstractCN:
                        return new List<Paragraph>(new Paragraph[] { ChineseAbstract });
                    case DocumentParts.abstractCN_Content:
                        return ChineseAbstractContents;
                    case DocumentParts.abstractEN_Content:
                        return EnglishAbstractContents;
                    case DocumentParts.abstractEN:
                        return new List<Paragraph>(new Paragraph[] { EnglishAbstract });
                    case DocumentParts.Acknowledgement:
                        return new List<Paragraph>(new Paragraph[] { Acknowledgement });
                    case DocumentParts.AcknowledgementContent:
                        return AcknowledgementContent;
                    case DocumentParts.Menu:
                        return new List<Paragraph>(new Paragraph[] { Menu });
                    case DocumentParts.MenuContent:
                        return MenuList;
                    case DocumentParts.Reference:
                        return new List<Paragraph>(new Paragraph[] { Reference });
                    case DocumentParts.ReferencedContent:
                        return ReferencedDocuments;
                    case DocumentParts.MainDocumentBody:
                    case DocumentParts.MainDocumentBodyInEnglish:
                        return MainDocumentBody;
                    case DocumentParts.CoverInfo:
                        return IsMultiMajor ? new List<Paragraph>(CoverMultiMajorInfoList) : new List<Paragraph>(CoverSingleMajorInfoList);
                    case DocumentParts.Cover:
                        return new List<Paragraph>(new Paragraph[] { Cover });
                    case DocumentParts.Appendix:
                        return new List<Paragraph>(new Paragraph[] { Appendix });
                    case DocumentParts.AppendixContent:
                        return AppendixContent;
                    case DocumentParts.Announcement:
                        return new List<Paragraph>(new Paragraph[] { Announcement });
                    case DocumentParts.AnnouncementContent:
                        return AnnouncementContent;
                    case DocumentParts.LviParagraph:
                        return LviParagraph[lvl];
                    default:
                        return new List<Paragraph>();
                }
            }
        }
    }
    public static class DocumentStructureHelper
    {

        static string[] Items = new string[]{"中文题目","外文题目","学    号","姓    名","年    级","专    业","系    别",
                                                         "学    院","双修专业","双修院系","指导教师","完成日期"};
        static int[] SingleMajorMapper = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        static int[] MultiMajorMapper = new int[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
        static string[] Keys = new string[] { "keyword:", "keywords:", "关键词:", "关键词：", "keyword：", "keywords：", "关键字：", "关键字:", "keyword", "keywords", "abstract", "摘要", "目录", "图片目录", "致谢", "参考文献", "关于南开大学本科生毕业论文（设计）的声明" };
        static string TitleInvalidTrailingChars = ".,，。;：:；!！??";
        static string InvalidNumberingEndingChars = ".。,，!?！？;；：:";
        public static bool IsParagraphEndsWithPunctuation(Paragraph p)
        {
            var tmp = p.InnerText.Trim();
            if (tmp == "") return false;
            return InvalidNumberingEndingChars.Contains(p.InnerText.Trim().Last());
        }
        /// <summary>
        /// The same as DocumentStructure.GetMappedId
        /// </summary>
        /// <param name="SingleMajor"></param>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public static int GetItemMapped(bool SingleMajor, string pattern)
        {
            string patternEx = pattern.Trim();
            if (patternEx == "") return -1;
            for (int i = 0; i < Items.Length; i++)
            {
                if (patternEx.StartsWith(Items[i] + ":") || patternEx.StartsWith(Items[i] + "："))
                {
                    if (SingleMajor)
                    {
                        if (i <= 7)
                        {
                            return i + 1;
                        }
                        else
                        {
                            return i - 1;
                        }
                    }
                    else
                    {
                        return i + 2;
                    }
                }
            }
            return -1;
        }

        public static int IsParagraphKeywordEx(Paragraph para)
        {
            var temp = para.InnerText.Replace(" ", "").Replace("\t", "").Replace("　", "").ToLowerInvariant();
            if (temp == "") return -1;
            for (int i = 0; i < Keys.Length; i++)
            {
                if (Keys[i].Contains(temp) || (temp.StartsWith(Keys[i]) && i <= 9))
                {
                    return i;
                }
            }
            return -1;
        }

        public static bool IsParagraphKeyword(Paragraph para)
        {
            return IsParagraphKeywordEx(para) != -1;
        }

        [Flags]
        public enum ParagraphValidityCheckOptions
        {
            None = 0,
            IgnoreWPSCode = 1,
            IgnoreFieldCode = 4,
            IgnoreNonStartingFieldCode = 2
        }

        private static bool CheckOptionsContains(ParagraphValidityCheckOptions checkAgainst, ParagraphValidityCheckOptions checkFor)
        {
            return (checkAgainst & checkFor) != ParagraphValidityCheckOptions.None;
        }

        public static bool IsParagraphInvalid(Paragraph para, ParagraphValidityCheckOptions fieldCodeCheckOption = ParagraphValidityCheckOptions.None)
        {
            if (CheckOptionsContains(fieldCodeCheckOption, ParagraphValidityCheckOptions.IgnoreWPSCode) == false)
            {
                if (para.InnerXml.Contains("<wps:"))
                {
                    return true;
                }
            }
            IEnumerable<Run> runInHyperLinks = para.Elements<Hyperlink>().SelectMany<Hyperlink, Run>(hyperlink =>
                    {
                        return hyperlink.Elements<Run>();
                    });
            if (para.Elements<Run>().Count() == 0)
            {
                if (runInHyperLinks.Count() == 0)
                {
                    return true;
                }
            }
            if (para.InnerText.Trim() == "")
            {
                return true;
            }

            if (CheckOptionsContains(fieldCodeCheckOption, ParagraphValidityCheckOptions.IgnoreFieldCode) == false)
            {
                foreach (Run r in runInHyperLinks)
                {
                    foreach (OpenXmlElement oxe in r.ChildElements)
                    {
                        if (oxe.GetType() == typeof(Text))
                        {
                            if (CheckOptionsContains(fieldCodeCheckOption, ParagraphValidityCheckOptions.IgnoreNonStartingFieldCode))
                            {
                                return false;
                            }
                        }
                        else if (oxe.GetType() == typeof(FieldCode))
                        {
                            return true;
                        }
                    }
                }

                foreach (Run r in para.Elements<Run>())
                {
                    foreach (OpenXmlElement oxe in r.ChildElements)
                    {
                        if (oxe.GetType() == typeof(Text))
                        {
                            if (CheckOptionsContains(fieldCodeCheckOption, ParagraphValidityCheckOptions.IgnoreNonStartingFieldCode))
                            {
                                return false;
                            }
                        }
                        else if (oxe.GetType() == typeof(FieldCode))
                        {
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        public static bool IsParagraphEndingWithTitleInalidTrailingChars(Paragraph p)
        {
            if (p.InnerText.Length == 0)
                return false;
            return TitleInvalidTrailingChars.Contains<char>(p.InnerText.Trim().Last());
        }

        private static Regex findLargestLvl = new Regex("(\\d+)", RegexOptions.Compiled);
        private static int GetLargestLevel(string lvlTxt)
        {
            if (!findLargestLvl.IsMatch(lvlTxt))
            {
                return -1;
            }
            if (lvlTxt.EndsWith("."))
            {
                return lvlTxt.Split('.').Length - 1;
            }
            return lvlTxt.Split('.').Length;
        }
        /// <summary>
        /// Gets the Paragraph's Level if it is set using Numbering in the context of the document structure.
        /// </summary>
        /// <param name="paragraph"></param>
        /// <param name="wd"></param>
        /// <param name="instNumCollection"></param>
        /// <param name="abstrNumCollection"></param>
        /// <param name="peek"></param>
        /// <returns></returns>
        public static int GetParagraphLevelNumbering(Paragraph paragraph, WordprocessingDocument wd, IEnumerable<NumberingInstance> instNumCollection, IEnumerable<AbstractNum> abstrNumCollection, ParagraphNumbering ParagraphNumbering, Dictionary<string, Style> styleDict, out NumberFormatValues value, bool peek = false, bool skipLevelTextCheck = false)
        {
            value = NumberFormatValues.None;
            if (paragraph == null)
            {
                return -1;
            }
            Paragraph p = paragraph;
            AbstractNum abstractNum;
            if (p.ParagraphProperties == null)
            {
                return -1;
            }
            if (p.ParagraphProperties.NumberingProperties != null)
            {
                NumberingProperties numberingProperties = p.ParagraphProperties.NumberingProperties;
                var refId = numberingProperties.NumberingId.Val.Value;
                var refilvlVal = 0;
                if (numberingProperties.NumberingLevelReference != null)
                {
                    refilvlVal = numberingProperties.NumberingLevelReference.Val;
                }
                var numDefPart = wd.MainDocumentPart.NumberingDefinitionsPart;
                string lvlText;
                int lvlRestart;
                int StartNumberingValue;
                Level level = ParagraphNumbering.GetResolvedLevel(refId, refilvlVal, abstrNumCollection, instNumCollection, styleDict, out abstractNum);
                if (level == null)
                {
                    return -1;
                }
                lvlText = level.LevelText.Val;
                lvlRestart = level.LevelRestart.Val;
                StartNumberingValue = level.StartNumberingValue.Val;
                if (!peek)
                {
                    ParagraphNumbering.AdjustNumberingInformation(abstractNum, level, abstrNumCollection, instNumCollection, p, styleDict);
                }
                value = level.NumberingFormat.Val;
                return refilvlVal + 1;

                //  ds.LviParagraph[segmentLength].Add(p);
                //  ds.TitleLevelMetaList.Add(segmentLength);
                //  ds.LviParagraph[0].Add(p);
            }
            if (paragraph.ParagraphProperties.ParagraphStyleId != null)
            {
                string styleID = paragraph.ParagraphProperties.ParagraphStyleId.Val;
                NumberingProperties numPr;
                int refId = 0;
                int refilvlVal = 0;
                var numDefPart = wd.MainDocumentPart.NumberingDefinitionsPart;
                string lvlText;
                int lvlRestart;
                int StartNumberingValue;
                Style current = styleDict[paragraph.ParagraphProperties.ParagraphStyleId.Val];

                if (current.StyleParagraphProperties != null)
                {
                    StyleParagraphProperties styleP = current.StyleParagraphProperties;
                    numPr = styleP.NumberingProperties;
                    if (numPr != null)
                    {
                        refilvlVal = 0;
                        if (numPr.NumberingLevelReference != null)
                        {
                            refilvlVal = numPr.NumberingLevelReference.Val;
                        }
                        if (numPr.NumberingId != null)
                        {
                            refId = numPr.NumberingId.Val;
                            Level level = ParagraphNumbering.GetResolvedLevel(refId, refilvlVal, abstrNumCollection, instNumCollection, styleDict, out abstractNum);
                            if (level == null)
                            {
                                return -1;
                            }
                            lvlText = level.LevelText.Val;
                            lvlRestart = level.LevelRestart.Val;
                            StartNumberingValue = level.StartNumberingValue.Val;
                            if (!peek)
                            {
                                ParagraphNumbering.AdjustNumberingInformation(abstractNum, level, abstrNumCollection, instNumCollection, p, styleDict);
                            }
                            value = level.NumberingFormat.Val;
                            return refilvlVal + 1;
                            //  ds.LviParagraph[segmentLength].Add(p);
                            //  ds.TitleLevelMetaList.Add(segmentLength);
                            //  ds.LviParagraph[0].Add(p);
                        }
                    }
                }
                while (current.BasedOn != null)
                {
                    styleID = current.BasedOn.Val;
                    current = styleDict[styleID];
                    if (current.StyleParagraphProperties != null)
                    {
                        StyleParagraphProperties styleP = current.StyleParagraphProperties;
                        numPr = styleP.NumberingProperties;
                        NumberingFormat numFmt;
                        if (numPr != null)
                        {
                            refilvlVal = 0;
                            if (numPr.NumberingLevelReference != null)
                            {
                                refilvlVal = numPr.NumberingLevelReference.Val;
                            }
                            if (numPr.NumberingId != null)
                            {

                                refId = numPr.NumberingId.Val;
                                Level level = ParagraphNumbering.GetResolvedLevel(refId, refilvlVal, abstrNumCollection, instNumCollection, styleDict, out abstractNum);
                                if (level == null)
                                {
                                    return -1;
                                }
                                lvlText = level.LevelText.Val;
                                lvlRestart = level.LevelRestart.Val;
                                StartNumberingValue = level.StartNumberingValue.Val;

                                if (!peek)
                                {
                                    ParagraphNumbering.AdjustNumberingInformation(abstractNum, level, abstrNumCollection, instNumCollection, p, styleDict);
                                }
                                value = level.NumberingFormat.Val;
                                return refilvlVal + 1;
                            }
                        }
                    }
                }
            }
            return -1;
        }
        /// <summary>
        /// Get the paragraph's level if the level is set manually using natural language.
        /// </summary>
        /// <param name="paragraph"></param>
        /// <param name="titlesReg"></param>
        /// <returns></returns>
        public static int GetParagraphLevelNonNumbering(Paragraph paragraph, Regex[] titlesReg)
        {
            if (paragraph == null)
            {
                return -1;
            }
            if (paragraph.InnerText.Trim() == "")
            {
                return -1;
            }
            Paragraph p = paragraph;
            for (int x = 0; x < titlesReg.Length - 1; x++)
            {
                if (titlesReg[x].IsMatch(p.InnerText))
                {
                    return x + 1;
                }
            }
            Match mc = titlesReg.Last<Regex>().Match(p.InnerText);
            if (mc.Success)
            {
                return GetLargestLevel(mc.Value);
            }
            return -1;
        }

        public static int GetParagraphOutlineLvl(Paragraph paragraph, Dictionary<string, Style> styleDict)
        {
            if (paragraph.ParagraphProperties == null)
            {
                return -1;
            }
            if (paragraph.ParagraphProperties.ParagraphStyleId == null)
            {
                return -1;
            }
            var styleId = paragraph.ParagraphProperties.ParagraphStyleId.Val.Value;
            var style = styleDict[styleId];
            if (style.StyleParagraphProperties == null)
            {
                return -1;
            }
            if (style.StyleParagraphProperties.OutlineLevel == null)
            {
                return -1;
            }
            return style.StyleParagraphProperties.OutlineLevel.Val;
        }
    }
    static class xtender
    {
        public static string _ToString(this IEnumerable<Paragraph> lists)
        {
            StringBuilder sb = new StringBuilder();
            foreach (Paragraph p in lists)
            {
                sb.Append(p.InnerText + "\n");
            }
            return sb.ToString();
        }
    }


}